package edu.pku.sei.transformation.compositeengine.engine;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;

import edu.pku.sei.transformation.editor.model.CompositeTransformation;
import edu.pku.sei.transformation.editor.model.TypedModel;
import edu.pku.sei.transformation.editor.model.execution.ControlFlow;
import edu.pku.sei.transformation.editor.model.execution.ControlNode;
import edu.pku.sei.transformation.editor.model.execution.TransformationExecution;
import edu.pku.sei.transformation.editor.views.problem.ProblemView;
import edu.pku.sei.transformation.execution.ExecutionController;
import edu.pku.sei.transformation.execution.ExecutionEnviroment;

public class CompositeTransformationEngine extends ExecutionController {
	protected CompositeTransformation transformation;
	private List<ControlNode> controlNodes = new ArrayList<ControlNode>();
	private List<TransformationExecution> remainTrans = new ArrayList<TransformationExecution>();
	private HashMap<ControlFlow, Integer> controlFlowStats = new HashMap<ControlFlow, Integer>();
	private static final Integer STATS_FAILED = 1;
	private static final Integer STATS_SUCCESS = 0;
	boolean reachEnd = false;
	
	public CompositeTransformationEngine(CompositeTransformation transformation,
			ExecutionEnviroment env) throws Exception {
		this.transformation = transformation;
		this.modelRoot = this.transformation.getModel();
		this.enviorment = env;
		this.executions.addAll(transformation.getSubExecutions());
		this.models.addAll(transformation.getIntermediateModels());
		for (TypedModel typedModel : transformation.getTypedModel()) {
			this.enviorment.pushData(typedModel);
		}
		this.controlNodes.addAll(transformation.getControlNodes());
	}
	
	@Override
	public boolean execute(IProgressMonitor monitor) {
		remainTrans.addAll(transformation.getSubExecutions());
		controlFlowStats.clear();
		reachEnd = false;
		
		ControlNode init = null;
		for (ControlNode node : controlNodes) {
			if (node.getNodeType() == ControlNode.CONTROL_INITIAL) {
				if (init == null) {
					init = node;
				} else {
					ProblemView.addError(modelRoot, ProblemView.EXECUTION, "control node", "more then one init node", transformation.getName());
					return false;
				}
			}
		}
		if (init == null) {
			ProblemView.addError(modelRoot, ProblemView.EXECUTION, "control node", "can not find init node", transformation.getName());
			return false;
		} else {
			for (Object obj : init.getOutgoings()) {
				if (obj instanceof ControlFlow) {
					markControlFlow((ControlFlow) obj, STATS_SUCCESS);
				}
			}
			boolean find = false;
			do {
				find = false;
				for (TransformationExecution trans : remainTrans) {
					if(getPrepared(trans)) {
						find = true;
						boolean succeed = execute(trans, monitor);
						if(succeed) {					
							for (Object obj : trans.getOutgoings()) {
								if (obj instanceof ControlFlow) {
									markControlFlow((ControlFlow) obj, STATS_SUCCESS);
								}
							}
						}
						else {
							ProblemView.addError(modelRoot, ProblemView.EXECUTION, "execution error", "one sub-transformation execute error", transformation.getName()+"->"+trans.getName()+":"+trans.getTransformation().getName());
							return false;
						}
						remainTrans.remove(trans);
						break;
					}
				}
			}while(find && remainTrans.size()>0 && reachEnd==false);
			if (reachEnd) {
				return true;
			} else if (remainTrans.size() == 0) {
				ProblemView.addWarning(modelRoot, ProblemView.EXECUTION, "control node", "transformation finished, but not meet final node", transformation.getName());
				return true;
			} else if (!find) {
				ProblemView.addError(modelRoot, ProblemView.EXECUTION, "execution error", "can not find executable sub-transformation while running", transformation.getName());
				return false;
			}
		}
		
		
		
		return false;
	}
	
	@Override
	protected boolean getPrepared(TransformationExecution transExe) {
		for (Object obj : transExe.getIncomings()) {
			if (obj instanceof ControlFlow && STATS_SUCCESS.equals(controlFlowStats.get((ControlFlow)obj))) {
				return super.getPrepared(transExe);
			}
		}
		return false;
	}
	
	protected void markControlFlow(ControlFlow flow, Integer stats) {
		controlFlowStats.put(flow, stats);
		
		Object target = flow.getTarget();
		if (target instanceof ControlNode) {
			ControlNode controlNode = (ControlNode) target;
			if (controlNode.getNodeType() == controlNode.CONTROL_INITIAL) {
				ProblemView.addError(modelRoot, ProblemView.EXECUTION, "control flow", "control flow point to initial node", transformation.getName());
			} else if (controlNode.getNodeType() == controlNode.CONTROL_FINAL) {
				reachEnd = true;
			} else if (controlNode.getNodeType() == controlNode.CONTROL_DECISION) {
				
			} else if (controlNode.getNodeType() == controlNode.CONTROL_MERGE) {
				for (Object obj : controlNode.getOutgoings()) {
					if (obj instanceof ControlFlow) {
						markControlFlow((ControlFlow) obj, stats);
					}
				}
			} else if (controlNode.getNodeType() == controlNode.CONTROL_FORK) {
				for (Object obj : controlNode.getOutgoings()) {
					if (obj instanceof ControlFlow) {
						markControlFlow((ControlFlow) obj, stats);
					}
				}
			} else if (controlNode.getNodeType() == controlNode.CONTROL_JOIN) {
				if (stats.equals(STATS_FAILED)) {
					for (Object obj : controlNode.getOutgoings()) {
						if (obj instanceof ControlFlow) {
							markControlFlow((ControlFlow) obj, STATS_FAILED);
						}
					}
				} else {
					for (Object obj : controlNode.getOutgoings()) {
						if (obj instanceof ControlFlow) {
							if (controlFlowStats.get(obj) == null) {
								return;
							} else if (controlFlowStats.get(obj).equals(STATS_FAILED)) {
								return;
							}
						}
					}
					for (Object obj : controlNode.getOutgoings()) {
						if (obj instanceof ControlFlow) {
							markControlFlow((ControlFlow) obj, STATS_SUCCESS);
						}
					}
				}
			}
		}
		
	}
	
}
